En esta práctica se desarrollan las siguientes competencias del Máster de Data Science:
Los objetivos concretos de esta práctica son:
Aprender a aplicar los conocimientos adquiridos y su capacidad de resolución de problemas en entornos nuevos o poco conocidos dentro de contextos más amplios o multidisciplinares.
Saber identificar los datos relevantes y los tratamientos necesarios (integración,limpieza y validación) para llevar a cabo un proyecto analítico.
Aprender a analizar los datos adecuadamente para abordar la información contenida en los datos.
Identificar la mejor representación de los resultados para aportar conclusiones sobre el problema planteado en el proceso analítico.
Actuar con los principios éticos y legales relacionados con la manipulación de datos en función del ámbito de aplicación.
Desarrollar las habilidades de aprendizaje que les permitan continuar estudiando de un modo que tendrá que ser en gran medida autodirigido o autónomo.
Desarrollar la capacidad de búsqueda, gestión y uso de información y recursos en el ámbito de la ciencia de datos.
El objetivo de esta actividad será el tratamiento de un dataset, que puede ser el creado en la Práctica 1 o bien cualquier dataset libre disponible en Kaggle https://www.kaggle.com.
Un ejemplo de dataset con el que podéis trabajar es el “Heart Attack Analysis & Prediction dataset”: https://www.kaggle.com/datasets/rashikrahmanpritom/heart-attack-analysis-prediction-dataset
Importante: si se elige un dataset diferente al propuesto es importante que este contenga una amplia variedad de datos numéricos y categóricos para poder realizar un análisis más rico y poder responder a las diferentes preguntas planteadas en el enunciado de la práctica.
Siguiendo las principales etapas de un proyecto analítico, las diferentes tareas a realizar (y justificar) son las siguientes:
Descripción del dataset. ¿Por qué es importante y qué pregunta/problema pretende responder?
Integración y selección de los datos de interés a analizar. Puede ser el resultado de adicionar diferentes datasets o una subselección útil de los datos originales, en base al objetivo que se quiera conseguir.
Limpieza de los datos.
3.1. ¿Los datos contienen ceros o elementos vacíos? Gestiona cada uno de estos casos.
3.2. Identifica y gestiona los valores extremos.
4.1. Selección de los grupos de datos que se quieren analizar/comparar (p.ej., si se van a comparar grupos de datos, ¿cuáles son estos grupos y qué tipo de análisis se van a aplicar?)
4.2. Comprobación de la normalidad y homogeneidad de la varianza.
4.3. Aplicación de pruebas estadísticas para comparar los grupos de datos. En función de los datos y el objetivo del estudio, aplicar pruebas de contraste de hipótesis, correlaciones, regresiones, etc. Aplicar al menos tres métodos de análisis diferentes.
Representación de los resultados a partir de tablas y gráficas. Este apartado se puede responder a lo largo de la práctica, sin necesidad de concentrar todas las representaciones en este punto de la práctica.
Resolución del problema. A partir de los resultados obtenidos, ¿cuáles son las conclusiones? ¿Los resultados permiten responder al problema?
Código. Hay que adjuntar el código, preferiblemente en R, con el que se ha realizado la limpieza, análisis y representación de los datos. Si lo preferís,también podéis trabajar en Python.
Vídeo. Realizar un breve vídeo explicativo de la práctica (máximo 10 minutos), donde ambos integrantes del equipo expliquen con sus propias palabras el desarrollo de la práctica, basándose en las preguntas del enunciado para justificar y explicar el código desarrollado. Este vídeo se deberá entregar a través de un enlace al Google Drive de la UOC (https://drive.google.com/…), junto con enlace al repositorio Git entregado.
En la práctica anterior, construimos en script capaz de extraer información de forma automatizada del Instituto nacional de estadística (INE), relativos al Atlas de distribución de renta de los hogares, estos indicadores incluyen datos como:
Estos datos los podemos extraer de forma agregada por provincia/municipio/distrito/sección, y año.
Los indicadores socio económicos son datos muy útiles en multitud de casos, especialmente en el ámbito público, como por ejemplo el caso descrito en (Bernat Goñi Ros, 2008) respecto a la identificaciones de sección censales desfavorecidas en la RMB. Una sección censal desfavorecida se define como “aquellas áreas urbanas espacialmente delimitadas en las cuales el fenómeno de la exclusión social afecta a amplios sectores de la población residente[2] (Conway & Konvitz, 2000), entendiendo la exclusión social como una acumulación de déficits vinculados al conocimiento, la información, el consumo cultural, la sanidad y las redes sociales que impiden a las personas que lo padecen acceder a bienes, derechos y oportunidades que se consideran básicos, participar en la vida social y económica, y constituirse como ciudadanos plenos (Subirats & Gomà, 2003). El concepto de exclusión social es más amplio que el de pobreza y reconoce que existen otros factores a parte de los bajos ingresos que pueden limitar la participación de las personas en la sociedad (Conway & Konvitz, 2000)”.
En base lo resultados de este estudio, los distintos stakeholders interesados como el ayuntamiento de Barcelona, La diputación de Barcelona, o la Generalitat de Catalunya, pueden llevar a cabo iniciativas para corregir estas situaciones de vulnerabilidad y revertir el grado de exclusión de las residentes de estas secciones.
Sin embargo, este estudio tiene una problemática, y es que se basa en datasets publicados que se publican cada decenio, lo cual nos impide realizar un seguimiento periódico de la evolución de la situación de exclusión de estas zonas.
Por ello, nos proponemos usar los indicares que podemos extraer del INE que tienen una periodicidad anual para crear un modelo con una capacidad de clasificación de sección censal desfavorecida similar a la que usa Bernat Goñi en su estudio.
A parte del dataset que hemos obtenido del INE usaremos tambien la fuente de (El nacional.cat)[https://elnacional.carto.com/tables/seccions_censals_barcelona/public/map] para obtener información ampliada sobre los las secciones censales de la ciudad de barcelona, en la que se amplian las secciones con sus correspondientes barrios y distritos.
En nuestro análisis realizaremos scrapping de los siguientes indicadores:
Mapa de distritos y barrios de Barcelona:
De © Sémhur / Wikimedia Commons, CC BY-SA 4.0, Enlace
Mapa de secciones censales vulnerables según estudio Bernat Goñi:
El detalle de los campos de este dataset es:
# Path del fichero csv con datos geoespaciales
renta_path <- "indicadores_renta.csv"
# Lecutra del csv
df_renta <- read.csv2(renta_path, header = TRUE)
# Seleccion de columans de interes
#df_sc_renta %<>% select(barrio, distrito, codi_sec_c, cod_mundis)
#Visualizacion del df
glimpse(df_renta)
## Rows: 160,308
## Columns: 6
## $ Municipios <chr> "08001 Abrera", "08001 Abrera", "…
## $ Distritos <chr> "", "", "", "", "", "", "", "", "…
## $ Secciones <chr> "", "", "", "", "", "", "", "", "…
## $ Indicadores.de.renta.media.y.mediana <chr> "Renta neta media por persona ", …
## $ Periodo <int> 2020, 2019, 2018, 2017, 2016, 201…
## $ Total <chr> "14.455", "14.347", "13.776", "13…
El detalle de los campos de este dataset es:
# Path del fichero csv con datos de fuente de ingresos
fuente_ingresos_path <- "indicadores_fuente_ingresos.csv"
# Lecutra del csv
df_fuente_ingresos <- read.csv2(fuente_ingresos_path, header = TRUE)
# Seleccion de columans de interes
#df_sc_renta %<>% select(barrio, distrito, codi_sec_c, cod_mundis)
#Visualizacion del df
glimpse(df_fuente_ingresos)
## Rows: 160,308
## Columns: 6
## $ Municipios <chr> "08001 Abrera", "08001 Abrera", "0…
## $ Distritos <chr> "", "", "", "", "", "", "", "", ""…
## $ Secciones <chr> "", "", "", "", "", "", "", "", ""…
## $ Distribución.por.fuente.de.ingresos <chr> "Renta bruta media por persona", "…
## $ Periodo <int> 2020, 2019, 2018, 2017, 2016, 2015…
## $ Total <chr> "17.579,0", "17.499,0", "16.748,0"…
El detalle de los campos de este dataset es:
# Path del fichero csv con datos de fuente de ingresos
demografia_path <- "indicadores_demograficos.csv"
# Lecutra del csv
df_demografia <- read.csv2(demografia_path, header = TRUE)
# Seleccion de columans de interes
#df_sc_renta %<>% select(barrio, distrito, codi_sec_c, cod_mundis)
#Visualizacion del df
glimpse(df_demografia)
## Rows: 187,068
## Columns: 6
## $ Municipios <chr> "08001 Abrera", "08001 Abrera", "08001 Abrera…
## $ Distritos <chr> "", "", "", "", "", "", "", "", "", "", "", "…
## $ Secciones <chr> "", "", "", "", "", "", "", "", "", "", "", "…
## $ Indicadores.demográficos <chr> "Edad media de la población", "Edad media de …
## $ Periodo <int> 2020, 2019, 2018, 2017, 2016, 2015, 2020, 201…
## $ Total <chr> "40,7", "40,3", "39,7", "39,5", "39,2", "38,8…
De este dataset publicado por El Nacional.cat, tenemos los datos geoespaciales de las secciones censales, lo que nos permitirá transformar estos códigos en información mas útil como de barrios y distritos.
La web donde extremos los datos no proporciona un detalle exhaustivo de los campos, pero tras un análisis visual del csv, observamos que podemos usar los siguientes atributos del mismo, ya que los nombres de las cabeceras son autoexplicativos y los datos contenidos son coherentes y el numero de secciones censales concuerda con la del dataframe anterior
Columnas de interés
# Path del fichero csv con datos geoespaciales
sc_path <- "seccions_censals_barcelona.csv"
# Lecutra del csv
df_sc_bcn <- read.csv(sc_path, header = TRUE)
# visualizacion cabecera
glimpse(df_sc_bcn)
## Rows: 1,068
## Columns: 16
## $ the_geom <chr> "0106000020E610000001000000010300000001000000E10200009813B4…
## $ cartodb_id <int> 672, 679, 354, 282, 221, 17, 37, 1029, 230, 267, 268, 298, …
## $ cod_sec_ce <int> 7090, 7097, 4013, 3055, 2167, 1017, 1038, 10104, 3003, 3040…
## $ mundissec <dbl> 8019307090, 8019307097, 8019304013, 8019303055, 8019302167,…
## $ codi_secci <int> 90, 97, 13, 55, 167, 17, 38, 104, 3, 40, 41, 71, 114, 44, 5…
## $ codi_distr <int> 7, 7, 4, 3, 2, 1, 1, 10, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4…
## $ codi_munic <int> 80193, 80193, 80193, 80193, 80193, 80193, 80193, 80193, 801…
## $ cod_mundis <int> 801907090, 801907097, 801904013, 801903055, 801902167, 8019…
## $ cod_barrio <int> 39, 40, 19, 15, 10, 1, 3, 71, 11, 14, 14, 17, 18, 14, 15, 1…
## $ cod_seccio <int> 90, 97, 13, 55, 167, 17, 38, 104, 3, 40, 41, 71, 114, 44, 5…
## $ cod_distri <int> 7, 7, 4, 3, 2, 1, 1, 10, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4…
## $ cod_mun <int> 8019, 8019, 8019, 8019, 8019, 8019, 8019, 8019, 8019, 8019,…
## $ cod_munici <int> 80193, 80193, 80193, 80193, 80193, 80193, 80193, 80193, 801…
## $ barrio <chr> "Sant Genís dels Agudells", "Montbau", "les Corts", "Hostaf…
## $ distrito <chr> "Horta-Guinardó", "Horta-Guinardó", "Les Corts", "Sants-Mon…
## $ codi_sec_c <int> 7090, 7097, 4013, 3055, 2167, 1017, 1038, 10104, 3003, 3040…
Del dataset seleccionado solo nos quedaremos con las variables de interés descritas anteriormente.
# Seleccion de columans de interes
df_sc_bcn_2 <- df_sc_bcn %>% select(barrio, distrito, codi_sec_c, cod_mundis,mundissec)
df_sc_bcn %<>% select(barrio, distrito, codi_sec_c, cod_mundis)
#Visualizacion del df
skim(df_sc_bcn)
| Name | df_sc_bcn |
| Number of rows | 1068 |
| Number of columns | 4 |
| _______________________ | |
| Column type frequency: | |
| character | 2 |
| numeric | 2 |
| ________________________ | |
| Group variables | None |
Variable type: character
| skim_variable | n_missing | complete_rate | min | max | empty | n_unique | whitespace |
|---|---|---|---|---|---|---|---|
| barrio | 0 | 1 | 5 | 44 | 0 | 73 | 0 |
| distrito | 0 | 1 | 6 | 19 | 0 | 10 | 0 |
Variable type: numeric
| skim_variable | n_missing | complete_rate | mean | sd | p0 | p25 | p50 | p75 | p100 | hist |
|---|---|---|---|---|---|---|---|---|---|---|
| codi_sec_c | 0 | 1 | 5784.43 | 2939.15 | 1001 | 3.04075e+03 | 6036.5 | 8092.25 | 10237 | ▇▆▆▇▇ |
| cod_mundis | 0 | 1 | 801905784.43 | 2939.15 | 801901001 | 8.01903e+08 | 801906036.5 | 801908092.25 | 801910237 | ▇▆▆▇▇ |
Vemos por lo tanto que se han cargado 3 variables, dos alfanuméricas (distrito y barrio) y otra numérica (codi_sec_c), y que contiene 1068 códigos censales únicos para la ciudad de Barcelona.
También observamos que hay definidos 73 barrios y 10 distritos, y que no existen datos en blanco que requieran de limpieza posterior.
Como podemos observar los datasets de renta, ingresos y demografía tienen las mismas columnas pero contienen diferentes medidas y niveles de agregación, en nuestro caso de estudio nos interesa analizar las clasificar las secciones censales de Barcelona en base a los datos mas recientes disponibles. Por lo tanto filtraremos estos datasets para quedarnos únicamente con los registros que concuerden con esta granularidad.
# Seleccionamos los datos de la ciudad de barcelona y del periodo de 2020
df_renta_clean <- df_renta %>% filter(grepl('*Barcelona*', Municipios) & Secciones != "" & Periodo == 2020 ) %>%
select(c("Secciones","Indicadores.de.renta.media.y.mediana", "Periodo","Total")) %>%
rename_at(c('Indicadores.de.renta.media.y.mediana','Total'), ~c('dimension','value'))
df_fuente_ingresos_clean <- df_fuente_ingresos %>%
filter(grepl('*Barcelona*', Municipios) & Secciones != "" & Periodo == 2020 ) %>%
select(c("Secciones","Distribución.por.fuente.de.ingresos", "Periodo","Total")) %>%
rename_at(c('Distribución.por.fuente.de.ingresos','Total'), ~c('dimension','value'))
# Filtramos la dimension Renta bruta media por persona ya que se encuentra contenida en df de renta
df_fuente_ingresos_clean %<>% filter(dimension != "Renta bruta media por persona")
df_demografia_clean <- df_demografia %>% filter(grepl('*Barcelona*', Municipios) & Secciones != "" & Periodo == 2020 ) %>%
select(c("Secciones","Indicadores.demográficos", "Periodo","Total")) %>%
rename_at(c('Indicadores.demográficos','Total'), ~c('dimension','value'))
# Unificamos los 3 datasets
df_indicadores <- rbind(df_renta_clean, df_demografia_clean, df_fuente_ingresos_clean)
head(df_indicadores)
## Secciones dimension
## 1 0801901001 Barcelona sección 01001 Renta neta media por persona
## 2 0801901001 Barcelona sección 01001 Renta neta media por hogar
## 3 0801901001 Barcelona sección 01001 Media de la renta por unidad de consumo
## 4 0801901001 Barcelona sección 01001 Mediana de la renta por unidad de consumo
## 5 0801901001 Barcelona sección 01001 Renta bruta media por persona
## 6 0801901001 Barcelona sección 01001 Renta bruta media por hogar
## Periodo value
## 1 2020 10.696
## 2 2020 27.503
## 3 2020 15.672
## 4 2020 12.950
## 5 2020 12.798
## 6 2020 32.909
Del bloque anterior observamos que en la columna de secciones tenemos una columna “Secciones” que contiene la información del código de municipio +código de sección seguido del nombre de municipio y de nuevo el codigo de ciudad.
Separaremos esta columna para poder identificar de forma unívoca el codigo censal.
df_indicadores[c('mundi_code', 'city_name','filler', 'sc_code')] <- str_split_fixed(df_indicadores$Secciones, ' ', 4)
head(df_indicadores)
## Secciones dimension
## 1 0801901001 Barcelona sección 01001 Renta neta media por persona
## 2 0801901001 Barcelona sección 01001 Renta neta media por hogar
## 3 0801901001 Barcelona sección 01001 Media de la renta por unidad de consumo
## 4 0801901001 Barcelona sección 01001 Mediana de la renta por unidad de consumo
## 5 0801901001 Barcelona sección 01001 Renta bruta media por persona
## 6 0801901001 Barcelona sección 01001 Renta bruta media por hogar
## Periodo value mundi_code city_name filler sc_code
## 1 2020 10.696 0801901001 Barcelona sección 01001
## 2 2020 27.503 0801901001 Barcelona sección 01001
## 3 2020 15.672 0801901001 Barcelona sección 01001
## 4 2020 12.950 0801901001 Barcelona sección 01001
## 5 2020 12.798 0801901001 Barcelona sección 01001
## 6 2020 32.909 0801901001 Barcelona sección 01001
Ya tenemos las columnas separadas, ahora procederemos a transformar y limpiar las columnas para facilitar su procesamiento.
# Transformamos las variables
df_indicadores_clean <- df_indicadores %>% mutate(
dimension = replace_non_ascii(mgsub(str_to_lower(str_trim(dimension)),c(" ",":"),c("_",""))),
value = as.numeric(gsub(",",".",gsub("\\.", "", value))),
mundi_code = as.factor(as.numeric(mundi_code)),
sc_code = as.factor(as.numeric(sc_code))
)
# Seleccionamos la columnas de interés
df_indicadores_clean %<>% select(c("city_name","mundi_code","sc_code","dimension","value"))
head(df_indicadores_clean)
## city_name mundi_code sc_code dimension value
## 1 Barcelona 801901001 1001 renta_neta_media_por_persona 10696
## 2 Barcelona 801901001 1001 renta_neta_media_por_hogar 27503
## 3 Barcelona 801901001 1001 media_de_la_renta_por_unidad_de_consumo 15672
## 4 Barcelona 801901001 1001 mediana_de_la_renta_por_unidad_de_consumo 12950
## 5 Barcelona 801901001 1001 renta_bruta_media_por_persona 12798
## 6 Barcelona 801901001 1001 renta_bruta_media_por_hogar 32909
Observamos como ahora tenemos el siguiente dataset tras la limpieza:
city_name: Nombre del municipio. mundi_code: Código municipio + sección censal. sc_code: Códifo de sección censal. dimension: Nombre de la dimensión. value: Valor de la dimension.
Del dataset de secciones censales transformaremos las columnas renombrandolas y convirtiendo los tipos de algunas columnas
df_sc_bcn_clean <- df_sc_bcn %>% rename_at(c('barrio','distrito', 'codi_sec_c', 'cod_mundis'), ~c('neighborhood','district','sc_code','mundi_code')) %>%
mutate_all(as.factor)
df_sc_bcn_2_clean <- df_sc_bcn_2 %>% rename_at(
c('barrio','distrito', 'codi_sec_c', 'cod_mundis'),
~c('neighborhood','district','sc_code','mundi_code')) %>%
mutate_all(as.factor)
head(df_sc_bcn_clean)
## neighborhood district sc_code mundi_code
## 1 Sant Genís dels Agudells Horta-Guinardó 7090 801907090
## 2 Montbau Horta-Guinardó 7097 801907097
## 3 les Corts Les Corts 4013 801904013
## 4 Hostafrancs Sants-Montjuïc 3055 801903055
## 5 Sant Antoni Eixample 2167 801902167
## 6 el Raval Ciutat Vella 1017 801901017
#### Dataset Final Desnormalizado
En base al dataset de indicadores y de secciones, crearemos un dataset final que los combine para tener toda la información que necesitamos en un único dataset. El data set resultante tendrá una forma de 20292 observaciones y 7 variables.
df_indicadores_longer <- df_indicadores_clean %>%
left_join(df_sc_bcn_clean, by = c("mundi_code","sc_code"))
glimpse(df_indicadores_longer)
## Rows: 19,224
## Columns: 7
## $ city_name <chr> "Barcelona", "Barcelona", "Barcelona", "Barcelona", "Barc…
## $ mundi_code <fct> 801901001, 801901001, 801901001, 801901001, 801901001, 80…
## $ sc_code <fct> 1001, 1001, 1001, 1001, 1001, 1001, 1002, 1002, 1002, 100…
## $ dimension <chr> "renta_neta_media_por_persona", "renta_neta_media_por_hog…
## $ value <dbl> 10696, 27503, 15672, 12950, 12798, 32909, 8362, 21913, 12…
## $ neighborhood <fct> "el Raval", "el Raval", "el Raval", "el Raval", "el Raval…
## $ district <fct> Ciutat Vella, Ciutat Vella, Ciutat Vella, Ciutat Vella, C…
Ahora ya tenemos un dataset con el que podremos trabajar, sin embargo vemos que las dimensiones están creadas como filas, para nuestro estudio queremos que cada una de estas dimensiones estén catalogadas en su propia columna para poder crear los modelos test estadisticos necesarios.
df_indicadores_wider <- df_indicadores_longer %>% pivot_wider(
names_from = dimension,
values_from = value,
values_fill = 0
)
glimpse(df_indicadores_wider)
## Rows: 1,068
## Columns: 23
## $ city_name <chr> "Barcelona", "Barcelona",…
## $ mundi_code <fct> 801901001, 801901002, 801…
## $ sc_code <fct> 1001, 1002, 1003, 1004, 1…
## $ neighborhood <fct> "el Raval", "el Raval", "…
## $ district <fct> Ciutat Vella, Ciutat Vell…
## $ renta_neta_media_por_persona <dbl> 10696, 8362, 8531, 10318,…
## $ renta_neta_media_por_hogar <dbl> 27503, 21913, 24220, 2740…
## $ media_de_la_renta_por_unidad_de_consumo <dbl> 15672, 12175, 12718, 1494…
## $ mediana_de_la_renta_por_unidad_de_consumo <dbl> 12950, 9450, 10150, 12250…
## $ renta_bruta_media_por_persona <dbl> 12798, 9727, 9932, 12318,…
## $ renta_bruta_media_por_hogar <dbl> 32909, 25493, 28198, 3271…
## $ edad_media_de_la_poblacion <dbl> 41.0, 38.3, 39.4, 38.4, 3…
## $ porcentaje_de_poblacion_menor_de_18_anos <dbl> 11.9, 11.5, 15.1, 14.4, 1…
## $ porcentaje_de_poblacion_de_65_y_mas_anos <dbl> 15.9, 11.6, 13.2, 10.5, 1…
## $ tamano_medio_del_hogar <dbl> 2, 2, 3, 3, 2, 2, 3, 3, 2…
## $ porcentaje_de_hogares_unipersonales <dbl> 39.4, 38.9, 33.7, 37.7, 3…
## $ poblacion <dbl> 1207, 1350, 2941, 2730, 2…
## $ porcentaje_de_poblacion_espanola <dbl> 54.1, 41.0, 52.3, 51.3, 5…
## $ fuente_de_ingreso_salario <dbl> 7242, 5779, 5838, 7980, 6…
## $ fuente_de_ingreso_pensiones <dbl> 2329, 1566, 1846, 1601, 1…
## $ fuente_de_ingreso_prestaciones_por_desempleo <dbl> 1201, 1285, 1076, 1159, 1…
## $ fuente_de_ingreso_otras_prestaciones <dbl> 564, 461, 569, 472, 638, …
## $ fuente_de_ingreso_otros_ingresos <dbl> 1463, 637, 603, 1106, 744…
Vemos ahora como tenemos un dataset que contiene 1068 observaciones(el mismo numero de secciones censales) y 23 colmunas. El detalle de las columnas del nuevo dataset es:
skim(df_indicadores_wider)
| Name | df_indicadores_wider |
| Number of rows | 1068 |
| Number of columns | 23 |
| _______________________ | |
| Column type frequency: | |
| character | 1 |
| factor | 4 |
| numeric | 18 |
| ________________________ | |
| Group variables | None |
Variable type: character
| skim_variable | n_missing | complete_rate | min | max | empty | n_unique | whitespace |
|---|---|---|---|---|---|---|---|
| city_name | 0 | 1 | 9 | 9 | 0 | 1 | 0 |
Variable type: factor
| skim_variable | n_missing | complete_rate | ordered | n_unique | top_counts |
|---|---|---|---|---|---|
| mundi_code | 0 | 1 | FALSE | 1068 | 801: 1, 801: 1, 801: 1, 801: 1 |
| sc_code | 0 | 1 | FALSE | 1068 | 100: 1, 100: 1, 100: 1, 100: 1 |
| neighborhood | 0 | 1 | FALSE | 73 | la : 40, San: 39, la : 36, les: 35 |
| district | 0 | 1 | FALSE | 10 | Eix: 173, San: 147, Hor: 123, Nou: 117 |
Variable type: numeric
| skim_variable | n_missing | complete_rate | mean | sd | p0 | p25 | p50 | p75 | p100 | hist |
|---|---|---|---|---|---|---|---|---|---|---|
| renta_neta_media_por_persona | 0 | 1 | 16821.36 | 4750.14 | 6594.0 | 13809.00 | 16274.00 | 18868.50 | 31670.0 | ▂▇▆▂▁ |
| renta_neta_media_por_hogar | 0 | 1 | 40648.09 | 12913.31 | 18862.0 | 32849.75 | 38113.50 | 44752.50 | 89731.0 | ▃▇▂▁▁ |
| media_de_la_renta_por_unidad_de_consumo | 0 | 1 | 24844.05 | 7749.74 | 10399.0 | 19969.75 | 23510.00 | 27516.50 | 51053.0 | ▃▇▃▁▁ |
| mediana_de_la_renta_por_unidad_de_consumo | 0 | 1 | 21619.05 | 5590.72 | 8750.0 | 17850.00 | 21350.00 | 24150.00 | 37450.0 | ▂▇▇▂▁ |
| renta_bruta_media_por_persona | 0 | 1 | 21371.39 | 7414.67 | 7328.0 | 16695.25 | 20106.00 | 23945.25 | 45312.0 | ▂▇▃▁▁ |
| renta_bruta_media_por_hogar | 0 | 1 | 51720.46 | 20165.29 | 23756.0 | 39762.75 | 47067.00 | 56869.00 | 127478.0 | ▇▇▂▁▁ |
| edad_media_de_la_poblacion | 0 | 1 | 44.54 | 2.60 | 34.8 | 43.00 | 44.70 | 46.20 | 53.8 | ▁▂▇▃▁ |
| porcentaje_de_poblacion_menor_de_18_anos | 0 | 1 | 14.46 | 3.07 | 6.4 | 12.60 | 13.90 | 15.90 | 31.9 | ▂▇▂▁▁ |
| porcentaje_de_poblacion_de_65_y_mas_anos | 0 | 1 | 22.00 | 5.10 | 5.7 | 18.90 | 22.00 | 25.30 | 40.2 | ▁▃▇▂▁ |
| tamano_medio_del_hogar | 0 | 1 | 2.20 | 0.40 | 2.0 | 2.00 | 2.00 | 2.00 | 3.0 | ▇▁▁▁▂ |
| porcentaje_de_hogares_unipersonales | 0 | 1 | 32.39 | 4.95 | 16.1 | 29.20 | 32.60 | 35.70 | 47.7 | ▁▃▇▅▁ |
| poblacion | 0 | 1 | 1487.04 | 340.33 | 672.0 | 1253.00 | 1447.50 | 1655.25 | 3187.0 | ▂▇▂▁▁ |
| porcentaje_de_poblacion_espanola | 0 | 1 | 80.06 | 10.07 | 19.0 | 76.40 | 81.85 | 86.60 | 98.1 | ▁▁▁▇▇ |
| fuente_de_ingreso_salario | 0 | 1 | 12299.40 | 3851.82 | 4320.0 | 9625.50 | 11980.00 | 14044.75 | 26063.0 | ▃▇▅▁▁ |
| fuente_de_ingreso_pensiones | 0 | 1 | 4326.29 | 1359.54 | 960.0 | 3419.00 | 4302.50 | 5119.00 | 8723.0 | ▂▆▇▂▁ |
| fuente_de_ingreso_prestaciones_por_desempleo | 0 | 1 | 786.89 | 165.07 | 315.0 | 697.75 | 786.00 | 872.00 | 1580.0 | ▁▇▅▁▁ |
| fuente_de_ingreso_otras_prestaciones | 0 | 1 | 739.11 | 128.87 | 363.0 | 650.75 | 735.00 | 816.00 | 1259.0 | ▁▇▇▂▁ |
| fuente_de_ingreso_otros_ingresos | 0 | 1 | 3193.18 | 3265.55 | 94.0 | 1250.75 | 2058.50 | 3550.75 | 14388.0 | ▇▂▁▁▁ |
Podemos observar como nuestra dataset no contiene valores nulos, también a partir de los percentiles e histogramas ser deduce de forma rápida que no existen valores extremos.
Nos interesa por lo tanto analizar si podemos realizar segmentación entre barrios que nos permite identificar secciones censales desfavorecidas/ vulnerables y en riesgo de serlo.
Pero antes de realizar esta segmentación nos preguntamos si realmente existe diferencias entre secciones censales y barrios de Barcelona.
Las hipótesis iniciales a las que querremos dar respuesta son:
Antes de avanzar con la selección de datos, en primera instancia queremos conocer la correlación cruzada entre las varaibles.
# Preseleccion de valores
df_indicadores_corr <- df_indicadores_wider %>% select(!c("city_name","mundi_code", "sc_code", "neighborhood", "district"))
# Calculamos la matriz de correlación
# Usamos la opción "pairwise.complete.obs" para evitar errores por posibles valores nulos
# de las variables de contaminación
df_indicadores_corr_mat <- round(cor(df_indicadores_corr, use="pairwise.complete.obs"),2)
# Generamos la matriz de correlación de p-values
df_indicadores_mat <- cor_pmat(df_indicadores_corr)
# Generamos el gráfico de correlaciones.
ggp1a <- ggcorrplot(df_indicadores_corr_mat, lab = TRUE, type = "lower", p.mat = df_indicadores_mat)
ggp1a
De este primer análisis de correlaciones cruzadas podemos concluir que:
Existe una gran correlación entre los múltiple indicadores de ingresos de renta. Esto tiene sentido ya que se puede llegar a deducir que los ingresos familiares tienen una fuerte dependencia con los ingresos personales. De la misma forma que la renta neta es un porcentaje directo de la renta bruta. Por lo tanto, nos quedaremos con solo una de estas variables, la cual sera la renta_neta_media_por_persona
La variable de “fuente_ingresos_otras_prestaciones” tiene no tiene influencia en el restos de variable, por lo cual la excluiremos de nuestro análisis.
Repetiremos el mismo análisis de correlación pero excluyendo las variables descritas anteriormente.
# Preseleccion de valores
df_indicadores_corr <- df_indicadores_wider %>%
select(!c("city_name",
"mundi_code",
"sc_code",
"neighborhood",
"district",
"renta_neta_media_por_hogar",
"media_de_la_renta_por_unidad_de_consumo",
"mediana_de_la_renta_por_unidad_de_consumo",
"renta_bruta_media_por_persona",
"renta_bruta_media_por_hogar",
"fuente_de_ingreso_otras_prestaciones"
))
# Calculamos la matriz de correlación
# Usamos la opción "pairwise.complete.obs" para evitar errores por posibles valores nulos
# de las variables de contaminación
df_indicadores_corr_mat <- round(cor(df_indicadores_corr, use="pairwise.complete.obs"),2)
# Generamos la matriz de correlación de p-values
df_indicadores_mat <- cor_pmat(df_indicadores_corr)
# Generamos el gráfico de correlaciones.
ggp1a <- ggcorrplot(df_indicadores_corr_mat, lab = TRUE, type = "lower", p.mat = df_indicadores_mat)
ggp1a
Sobre las primeras hipótesis planteadas crearemos los dataframes que nos permitan dar respuesta las mismas
df_indicadores_wider %>%
group_by(district, neighborhood) %>%
dplyr::summarize(renta_neta_media_por_persona = mean(renta_neta_media_por_persona )) %>%
ggplot(aes(x = reorder(neighborhood,renta_neta_media_por_persona,na.rm = TRUE), y = renta_neta_media_por_persona, fill = district)) +
geom_bar(stat="identity") + theme_test() + labs(title = "Renta media por persona por barrrio") +
xlab("Barrio") + ylab("Renta media por persona") + scale_color_discrete(name= "Distrito") +
theme(axis.text.x=element_text(angle=90,hjust=1,vjust=0.5), legend.position = "top")
## `summarise()` has grouped output by 'district'. You can override using the
## `.groups` argument.
df_indicadores_wider %>%
group_by(district) %>%
dplyr::summarize(renta_neta_media_por_persona = mean(renta_neta_media_por_persona )) %>%
ggplot(aes(x = reorder(district,renta_neta_media_por_persona), y = renta_neta_media_por_persona, fill=district)) +
geom_bar(stat="identity") + theme_test() + labs(title = "Renta media por persona por distrito") +
xlab("Barrio") + ylab("Renta media por hogar") + scale_color_discrete(name= "Distrito") +
theme(axis.text.x=element_text(angle=90,hjust=1,vjust=0.5), legend.position = "none")
A nivel visual podemos intuir que sí existe diferencias a nivel de distrito y barrio, con la mayor diferencia produciéndose entre el distrito de Sarria-SantGervasi respecto al distrito de ciutat bella. Pero realicemos un test estadístico de ANOVA para validar esta hipótesis.
anova_ingresos <- aov(renta_neta_media_por_persona ~ district, data = df_indicadores_wider)
summary(anova_ingresos)
## Df Sum Sq Mean Sq F value Pr(>F)
## district 9 1.531e+10 1.702e+09 205.5 <2e-16 ***
## Residuals 1058 8.761e+09 8.281e+06
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Podemos observar que el valor de Pr(>F) es inferior al nivel de significancia por lo que se confirma que existe diferencia entre las medias de renta entre el distrito al que pertenece una persona y su ingresos netos.
Continuemos ahora con el análisis de la segunda cuestión, para ello visualizaremos la distribución de porcentaje de población envejecida respecto al porcentaje de población extranjera y el nivel de renta.
df_indicadores_wider %>%
group_by(district, neighborhood) %>%
dplyr::summarize(
porcentaje_de_poblacion_de_65_y_mas_anos = mean(porcentaje_de_poblacion_de_65_y_mas_anos),
porcentaje_de_poblacion_espanola = mean(porcentaje_de_poblacion_espanola),
renta_neta_media_por_hogar = mean(renta_neta_media_por_hogar)
) %>%
ggplot(aes(
x = porcentaje_de_poblacion_de_65_y_mas_anos,
y = porcentaje_de_poblacion_espanola,
size = renta_neta_media_por_hogar,
color = district,
label = neighborhood
)) +
geom_point() + theme_test() + labs(title = "Scatter pobalción española y edad respecto ingresos") +
xlab("Porcentaje de población de mas de 65 años") + ylab("Porcentaje población española") + scale_color_discrete(name= "Distrito") +
theme(legend.position = "bottom", legend.box = "vertical")
## `summarise()` has grouped output by 'district'. You can override using the
## `.groups` argument.
En este caso, extraer conclusiones de la visualización de los datos puede ser algo mas complejo. A simple vista, podemos intuir que a partir de cierto umbral (aprox por debajo del 70%) el porcentaje de población española influye en la renta media per cápita, también se puede observar como en determinada franja de porcentaje de población mayor de 65 años (entre un 20% y un 25%) también existe una diferenciación en ingresos respecto a las otras franjas.
Validemos ahora mediante el test Anova de 2 direcciones si existe diferencia en las medias entre estas tres variables.
anova_demografia <- aov(renta_neta_media_por_persona ~ porcentaje_de_poblacion_de_65_y_mas_anos + porcentaje_de_poblacion_espanola, data = df_indicadores_wider)
summary(anova_demografia)
## Df Sum Sq Mean Sq F value
## porcentaje_de_poblacion_de_65_y_mas_anos 1 1.786e+09 1.786e+09 94.91
## porcentaje_de_poblacion_espanola 1 2.255e+09 2.255e+09 119.89
## Residuals 1065 2.003e+10 1.881e+07
## Pr(>F)
## porcentaje_de_poblacion_de_65_y_mas_anos <2e-16 ***
## porcentaje_de_poblacion_espanola <2e-16 ***
## Residuals
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Vemos como también en esta caso el p-value es lo suficientemente reducido como para rechazar la hipótesis de nula de que no existe diferencia entre la medias.
Veremos ahora si los modelos ANOVA generados cumplen con la condición homocedasticidad para ello, haremos uso los gráficos “Residual vs Fitted” y “QQ-plot”
# Ploteamos el diagrama de residuos vs valores ajustados
plot(anova_demografia, which = 1)
plot(anova_demografia, which = 2)
Estos gráficos nos permiten obtener información sobre:
Si hemos utilizado el tipo de relación adecuada (si el modelo debería ser no lineal en lugar de lineal).el resultado del modelo ajustado (linea roja) debe rondar la recta horizontal. En nuestro caso vemos que la linea roja se aproxima a la recta.
Si el QQplot de una regresión de un modelo teórico perfectamente homocedastatico vs el obtenido en la realidad. En este caso vemos que se aprxima pero con episodio de colas pesadas a la derecha.
En base al análisis visual resulta difícil afirmar que cumple la condición hemocedasticidad.
# Ploteamos el diagrama de residuos vs valores ajustados
plot(anova_ingresos, which = 1)
plot(anova_ingresos, which = 2)
En el segundo modelo, si que observamos que el gráfico de QQ se aproxima mucho a la recta y el gráfico de Residuals vs Fitted la linea roja se aproxima a la recta 0. Por lo que en este caso si que podemos afirmar que la condicion de hemocedasticidad.
En nuestro problema planteado queremos realizar un modelo clasificador que me permita identificar secciones censales desfavorecidas/ vulnerables. Por lo cual como primer paso debemos realizar una normalización de los datos con los que trabajaremos y posteriormente realizar una reducción de la dimensionalidad con técnicas como la PCA o UMAP.
# Preseleccion de valores
df_indicadores_work <- df_indicadores_wider %>%
select(!c(
"city_name",
# "mundi_code",
"sc_code",
"neighborhood",
"district",
"renta_neta_media_por_hogar",
"media_de_la_renta_por_unidad_de_consumo",
"mediana_de_la_renta_por_unidad_de_consumo",
"renta_bruta_media_por_persona",
"renta_bruta_media_por_hogar",
"fuente_de_ingreso_otras_prestaciones"
))
# Normalizamos los valores para creacion del modelo
df_indicadores_norm <- df_indicadores_work %>%
column_to_rownames("mundi_code") %>%
scale()
# Visualizacion
df_indicadores_norm[0:3,0:3]
## renta_neta_media_por_persona edad_media_de_la_poblacion
## 801901001 -1.289510 -1.362498
## 801901002 -1.780863 -2.401394
## 801901003 -1.745285 -1.978140
## porcentaje_de_poblacion_menor_de_18_anos
## 801901001 -0.8341058
## 801901002 -0.9644005
## 801901003 0.2082519
set.seed(42)
umap_fit <- umap(df_indicadores_norm)
df_indicadores_umap <- umap_fit$layout %>%
as.data.frame() %>%
rownames_to_column("mundi_code") %>%
inner_join(
df_indicadores_wider %>% select(!where(is.numeric)),by="mundi_code"
)
head(df_indicadores_umap)
## mundi_code V1 V2 city_name sc_code neighborhood district
## 1 801901001 -3.173987 -0.4862879 Barcelona 1001 el Raval Ciutat Vella
## 2 801901002 -3.391444 -0.7430959 Barcelona 1002 el Raval Ciutat Vella
## 3 801901003 -5.298902 -7.3308856 Barcelona 1003 el Raval Ciutat Vella
## 4 801901004 -5.345277 -7.3635378 Barcelona 1004 el Raval Ciutat Vella
## 5 801901005 -3.184929 -0.1104940 Barcelona 1005 el Raval Ciutat Vella
## 6 801901006 -3.390326 -0.6969324 Barcelona 1006 el Raval Ciutat Vella
Del dataframe resultante podemos observar como ya tenemos dos variables V1, y V2 que nos permitirá observar como se distribuyen en un espacio reducido de dos dimensiones las segmentaciones por barrios.
df_indicadores_umap %>%
ggplot(aes(
x = V1,
y = V2,
color = district,
shape = district
)) +
geom_point() + theme_test() + labs(title = "Scatter indicadores UMAP") +
xlab("V1") + ylab("V2") +
scale_color_discrete(name= "Distrito") +
scale_shape_manual(values=c(0:9), name = "Distrito") +
theme(legend.position = "bottom", legend.box = "vertical")
Vemos que se aprecian de forma visual 4 clusters diferentes.
A simple vista podemos llegar a interpretar que:
Comprobemos si las segmentaciones que pretendemos realizar mediante la aplicación de varios algoritmos.
df_cluster <- df_indicadores_umap %>% select(V1,V2)
DBSCAN
Probaremos en como primera opción el algoritmo DBSCAN, por lo que debemos encontrar un valor apropiado para la variable epsilon
kNNdistplot(df_indicadores_umap %>% select(V1,V2), k = 9)
abline(h = 0.47, lty = 2)
En base al gráfico anterior podemos extraer que el valor optimo de epsilon para K = 9 es 0,47. Vemos ahora los clusters que se generan.
# Asignacion de clusters
umap_fit_db <- dbscan(df_cluster, eps = 0.47, minPts = 9)
print(umap_fit_db)
## DBSCAN clustering for 1068 objects.
## Parameters: eps = 0.47, minPts = 9
## Using euclidean distances and borderpoints = TRUE
## The clustering contains 5 cluster(s) and 5 noise points.
##
## 0 1 2 3 4 5
## 5 53 149 704 95 62
##
## Available fields: cluster, eps, minPts, dist, borderPoints
# Visualización de clusters
fviz_cluster(umap_fit_db, df_cluster, stand = FALSE, frame = FALSE, geom = "point")
## Warning: argument frame is deprecated; please use ellipse instead.
Observamos como los clusters generados encajan con las primera inspección visual que realizamos, es más, incluso a identificado el subcluster que se intuía en el cuadrante superior derecho.
K-Means
Probaremos también el algoritmo de clusterizacion clásico de k-means. Asignaremos un valor de k = 5, para intentar encontrar un resultado similar al anterior.
# Cluster de 5
umap_fit_pam <- pam(df_cluster, 5, metric = "euclidean", stand = FALSE)
# Visualizacion de cluster de 5
umap_pam <- fviz_cluster(umap_fit_pam, data = df_cluster ) + ggtitle("K = 5")
# Plots
umap_pam
En este caso, vemos como se agrupan en mismo cluster, barrios pertenecientes a rentas altas y bajas, por lo que el resultado la aplicación del algoritmo k-means no parece el mas adecuado. Esto puede deberse que la forma de los clusters no es uniforme, este cluster es muy eficaz para identificar patrones elípticos.
A raíz de la comparativa entre las segmentaciones realizadas por los algoritmos de DBSCAN y K-means nos decantaremos por las resultantes de DBSCAN ya que concuerdan de manera mas correcta las observaciones empíricas que conocemos sobre los barrios incluidos en estos clusters y el mapa de desfaborecimiento dentro del estudio de Bernat Goñi Ros.
En concreto nos interesarán los clusters 1 y 2 para generar un mapa de secciones censales desfavorecidas/ vulnerables. Veamos por lo tanto cuantas y cuales son estas secciones.
Añadiremos el resultado de la clusterización al DF original para poder visualizarlos en el mapa.
df_indicadores_wider$cluster = umap_fit_db$cluster
glimpse(df_indicadores_wider)
## Rows: 1,068
## Columns: 24
## $ city_name <chr> "Barcelona", "Barcelona",…
## $ mundi_code <fct> 801901001, 801901002, 801…
## $ sc_code <fct> 1001, 1002, 1003, 1004, 1…
## $ neighborhood <fct> "el Raval", "el Raval", "…
## $ district <fct> Ciutat Vella, Ciutat Vell…
## $ renta_neta_media_por_persona <dbl> 10696, 8362, 8531, 10318,…
## $ renta_neta_media_por_hogar <dbl> 27503, 21913, 24220, 2740…
## $ media_de_la_renta_por_unidad_de_consumo <dbl> 15672, 12175, 12718, 1494…
## $ mediana_de_la_renta_por_unidad_de_consumo <dbl> 12950, 9450, 10150, 12250…
## $ renta_bruta_media_por_persona <dbl> 12798, 9727, 9932, 12318,…
## $ renta_bruta_media_por_hogar <dbl> 32909, 25493, 28198, 3271…
## $ edad_media_de_la_poblacion <dbl> 41.0, 38.3, 39.4, 38.4, 3…
## $ porcentaje_de_poblacion_menor_de_18_anos <dbl> 11.9, 11.5, 15.1, 14.4, 1…
## $ porcentaje_de_poblacion_de_65_y_mas_anos <dbl> 15.9, 11.6, 13.2, 10.5, 1…
## $ tamano_medio_del_hogar <dbl> 2, 2, 3, 3, 2, 2, 3, 3, 2…
## $ porcentaje_de_hogares_unipersonales <dbl> 39.4, 38.9, 33.7, 37.7, 3…
## $ poblacion <dbl> 1207, 1350, 2941, 2730, 2…
## $ porcentaje_de_poblacion_espanola <dbl> 54.1, 41.0, 52.3, 51.3, 5…
## $ fuente_de_ingreso_salario <dbl> 7242, 5779, 5838, 7980, 6…
## $ fuente_de_ingreso_pensiones <dbl> 2329, 1566, 1846, 1601, 1…
## $ fuente_de_ingreso_prestaciones_por_desempleo <dbl> 1201, 1285, 1076, 1159, 1…
## $ fuente_de_ingreso_otras_prestaciones <dbl> 564, 461, 569, 472, 638, …
## $ fuente_de_ingreso_otros_ingresos <dbl> 1463, 637, 603, 1106, 744…
## $ cluster <int> 1, 1, 2, 2, 1, 1, 2, 2, 1…
df_indicadores_wider %>%
group_by(cluster) %>% dplyr::summarise(count_cluster = n())
## # A tibble: 6 × 2
## cluster count_cluster
## <int> <int>
## 1 0 5
## 2 1 53
## 3 2 149
## 4 3 704
## 5 4 95
## 6 5 62
Vemos como para los clusters 1 y 2 tenemos un total 202 secciones censales. Para su representacion en el plano haremos uso de los datos geoespaciales que proporciona el Institut Cartogràfic i Geològic de Catalunya.
Cargaremos las shapes en un dataframe para su procesamiento y filtramos las secciones del municipio de Barcelona (080193).
# https://www.icgc.cat/es/Administracion-y-empresa/Descargas/Capas-de-geoinformacion/Secciones-censales
df_geom_shape <- st_read("bseccenv10sh1f1_20210101_1") %>%
mutate(mundissec = as.factor(as.numeric(MUNDISSEC))) %>%
filter(MUNICIPI == "080193")
## Reading layer `bseccenv10sh1f1_20210101_1' from data source
## `/Users/cospina/Library/CloudStorage/OneDrive-Personal/Educacion/UOC/4 - 2do Semestre 2022/Tipología y ciclo de vida de los datos/PRA/PRA 2/R_PRA_2/PRA_2/bseccenv10sh1f1_20210101_1'
## using driver `ESRI Shapefile'
## Simple feature collection with 5083 features and 4 fields
## Geometry type: MULTIPOLYGON
## Dimension: XY
## Bounding box: xmin: 260160.2 ymin: 4488767 xmax: 527397.6 ymax: 4747976
## Projected CRS: ETRS89 / UTM zone 31N
head(df_geom_shape)
## Simple feature collection with 6 features and 5 fields
## Geometry type: MULTIPOLYGON
## Dimension: XY
## Bounding box: xmin: 430267.6 ymin: 4580196 xmax: 431677.2 ymax: 4581413
## Projected CRS: ETRS89 / UTM zone 31N
## MUNICIPI DISTRICTE SECCIO MUNDISSEC geometry
## 1 080193 01 001 08019301001 MULTIPOLYGON (((431076.9 45...
## 2 080193 01 002 08019301002 MULTIPOLYGON (((431023.5 45...
## 3 080193 01 003 08019301003 MULTIPOLYGON (((430779 4580...
## 4 080193 01 004 08019301004 MULTIPOLYGON (((430631.3 45...
## 5 080193 01 005 08019301005 MULTIPOLYGON (((430938.5 45...
## 6 080193 01 006 08019301006 MULTIPOLYGON (((430873.3 45...
## mundissec
## 1 8019301001
## 2 8019301002
## 3 8019301003
## 4 8019301004
## 5 8019301005
## 6 8019301006
Tras la carga del dataframe geoespacial, lo enriqueceremos con nuestro dataframe de secciones censales y el dataframe que contiene los indicadores y los clusters asignados.
df_sc_temp <- df_sc_bcn_2_clean %>% select(mundi_code,mundissec)
df_sc_desfavorecidas_geom <- df_geom_shape %>%
left_join(df_sc_temp,by="mundissec") %>%
left_join(df_indicadores_wider,by="mundi_code")
glimpse(df_sc_desfavorecidas_geom)
## Rows: 1,068
## Columns: 30
## $ MUNICIPI <chr> "080193", "080193", "0801…
## $ DISTRICTE <chr> "01", "01", "01", "01", "…
## $ SECCIO <chr> "001", "002", "003", "004…
## $ MUNDISSEC <chr> "08019301001", "080193010…
## $ mundissec <fct> 8019301001, 8019301002, 8…
## $ mundi_code <fct> 801901001, 801901002, 801…
## $ city_name <chr> "Barcelona", "Barcelona",…
## $ sc_code <fct> 1001, 1002, 1003, 1004, 1…
## $ neighborhood <fct> "el Raval", "el Raval", "…
## $ district <fct> Ciutat Vella, Ciutat Vell…
## $ renta_neta_media_por_persona <dbl> 10696, 8362, 8531, 10318,…
## $ renta_neta_media_por_hogar <dbl> 27503, 21913, 24220, 2740…
## $ media_de_la_renta_por_unidad_de_consumo <dbl> 15672, 12175, 12718, 1494…
## $ mediana_de_la_renta_por_unidad_de_consumo <dbl> 12950, 9450, 10150, 12250…
## $ renta_bruta_media_por_persona <dbl> 12798, 9727, 9932, 12318,…
## $ renta_bruta_media_por_hogar <dbl> 32909, 25493, 28198, 3271…
## $ edad_media_de_la_poblacion <dbl> 41.0, 38.3, 39.4, 38.4, 3…
## $ porcentaje_de_poblacion_menor_de_18_anos <dbl> 11.9, 11.5, 15.1, 14.4, 1…
## $ porcentaje_de_poblacion_de_65_y_mas_anos <dbl> 15.9, 11.6, 13.2, 10.5, 1…
## $ tamano_medio_del_hogar <dbl> 2, 2, 3, 3, 2, 2, 3, 3, 2…
## $ porcentaje_de_hogares_unipersonales <dbl> 39.4, 38.9, 33.7, 37.7, 3…
## $ poblacion <dbl> 1207, 1350, 2941, 2730, 2…
## $ porcentaje_de_poblacion_espanola <dbl> 54.1, 41.0, 52.3, 51.3, 5…
## $ fuente_de_ingreso_salario <dbl> 7242, 5779, 5838, 7980, 6…
## $ fuente_de_ingreso_pensiones <dbl> 2329, 1566, 1846, 1601, 1…
## $ fuente_de_ingreso_prestaciones_por_desempleo <dbl> 1201, 1285, 1076, 1159, 1…
## $ fuente_de_ingreso_otras_prestaciones <dbl> 564, 461, 569, 472, 638, …
## $ fuente_de_ingreso_otros_ingresos <dbl> 1463, 637, 603, 1106, 744…
## $ cluster <int> 1, 1, 2, 2, 1, 1, 2, 2, 1…
## $ geometry <MULTIPOLYGON [m]> MULTIPOLYGON…
Observamos como ahora tenemos un dataframe final con toda la información geoespacial, indicadores y clusters. Para finalizar crearemos un mapa donde se muestren como se distribuyen por la ciudad los clusters.
# Plot del geom
sfmap <-ggplot()+
geom_sf(data=df_sc_desfavorecidas_geom, aes(fill=as.factor(cluster)),color = "white")+
coord_sf(datum = NA) +
theme_void() +
theme(legend.key.size = unit(1,"line"), legend.key.height= unit(0.5,"line")) +
scale_fill_manual(values = c("grey","red", "orange", "#D4F1F4","#189AB4", "#05445E"), name= "Cluster")+
labs(title="Clusters de secciones censales")
sfmap
Podemos observar como el mapa de distribución de clusters generados sobre secciones censales valida la hipotesis teórica sobre las etiquetas que podemos asignar a estos clusters si lo contrastamos con conocimientos empíricos y del estudio de Goñi y su mapa de secciones censales desfavorecidas/ vulnerables.
Por lo tanto de nuestras secciones podemos etiquetarlas como:
- Cluster 1 y 2: Secciones censales vulnerables.
- Cluster 3: Secciones censales no vulnerables.
- Cluster 4 y 5: Secciones favorecidas.
Damos con esto como conseguido el objetivo de nuestro estudio, que es el de usar datos de periodicidad anual para poder obtener sobre la distribución de secciones vulnerables intervales temporales mas cortos, que permitan a las entidades públicas dedicar los recursos necesarios para reconducir estas situaciones de vulnerabilidad de forma mas eficiente, con la intención de seguir y medir el resultado de las iniciativas de forma mas certera.
Para finalizar exportaremos el dataset final a un csv para ponerlo al alcance de cualquier interesado.
write.table(df_indicadores_wider, "secciones_censales_vulnerables.csv", row.names=FALSE, sep = ";", quote = TRUE, )
[1] Bernat Goñi Ros, IDENTIFICACIÓN, LOCALIZACIÓN Y CARACTERIZACIÓN DE LAS SECCIONES CENSALES DESFAVORECIDAS DE LA REGIÓN METROPOLITANA DE BARCELONA https://www.ub.edu/geocrit/sn/sn-272.htm [2008]
[2] CONWAY, Maureen y KONVITZ, Joseph. Meeting the challenge of distressed urban areas. Urban Studies, 2000, vol. 37, nº 4, pp. 749-774.
[3] SUBIRATS, J. y GOMÀ, R. Un paso más hacia la inclusión social. Generación de conocimiento, políticas y prácticas para la inclusión social. [Artículo en línea] Madrid: Acción Social, 2003. http://www.plataformaongs.org/Documental/VisorDocumentos.asp?id=43 [2 de noviembre de 2006].